package com.fsserver.config.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fsserver.common.utils.MDCTraceUtils;
import com.fsserver.common.utils.PaymentLogUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

/**
 * 支付服务切面
 * 记录支付服务方法调用
 * 
 * @author fsserver
 * @date 2025-03-13
 */
@Aspect
@Component
public class PaymentServiceAspect {
    private static final Logger log = LoggerFactory.getLogger(PaymentServiceAspect.class);
    
    @Autowired
    private ObjectMapper objectMapper;
    
    /**
     * 定义切点
     * 拦截支付服务实现类中的方法
     */
    @Pointcut("execution(* com.fsserver.core.service.impl.*PaymentServiceImpl.*(..))")
    public void paymentService() {}
    
    /**
     * 定义切点
     * 拦截支付订单服务实现类中的方法
     */
    @Pointcut("execution(* com.fsserver.core.service.impl.PaymentOrderServiceImpl.*(..))")
    public void paymentOrderService() {}
    
    /**
     * 环绕通知：记录支付服务方法调用
     */
    @Around("paymentService()")
    public Object aroundPaymentService(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        
        // 提取方法参数
        Map<String, Object> paramMap = extractMethodParams(joinPoint);
        String paymentType = getStringParam(paramMap, "paymentType");
        String orderNo = getStringParam(paramMap, "orderNo");
        
        // 设置业务ID
        setBusinessId(orderNo);
        
        // 记录方法开始
        if (log.isInfoEnabled()) {
            try {
                log.info("支付服务方法开始 - 方法: {}, 支付类型: {}, 订单号: {}, 参数: {}", 
                        methodName, paymentType, orderNo, toJson(paramMap));
            } catch (Exception e) {
                log.warn("记录方法开始日志异常", e);
            }
        }
        
        // 使用 StopWatch 计时
        StopWatch stopWatch = new StopWatch(methodName);
        stopWatch.start();
        Object result = null;
        
        try {
            // 执行原方法
            result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            // 记录异常
            PaymentLogUtils.logPaymentError(paymentType, orderNo, "支付服务方法异常: " + methodName, e);
            throw e;
        } finally {
            stopWatch.stop();
            // 记录方法结束
            if (log.isInfoEnabled()) {
                try {
                    log.info("支付服务方法结束 - 方法: {}, 支付类型: {}, 订单号: {}, 耗时: {} ms, 结果: {}", 
                            methodName, paymentType, orderNo, stopWatch.getTotalTimeMillis(), 
                            result != null ? toJson(result) : "null");
                } catch (Exception e) {
                    log.warn("记录方法结束日志异常", e);
                }
            }
        }
    }
    
    /**
     * 环绕通知：记录支付订单服务方法调用
     */
    @Around("paymentOrderService()")
    public Object aroundPaymentOrderService(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        
        // 提取方法参数
        Map<String, Object> paramMap = extractMethodParams(joinPoint);
        String orderNo = getStringParam(paramMap, "orderNo");
        
        // 设置业务ID
        setBusinessId(orderNo);
        
        // 记录方法开始
        if (log.isInfoEnabled()) {
            try {
                log.info("支付订单服务方法开始 - 方法: {}, 订单号: {}, 参数: {}", 
                        methodName, orderNo, toJson(paramMap));
            } catch (Exception e) {
                log.warn("记录方法开始日志异常", e);
            }
        }
        
        // 使用 StopWatch 计时
        StopWatch stopWatch = new StopWatch(methodName);
        stopWatch.start();
        Object result = null;
        
        try {
            // 执行原方法
            result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            // 记录异常
            PaymentLogUtils.logPaymentError(null, orderNo, "支付订单服务方法异常: " + methodName, e);
            throw e;
        } finally {
            stopWatch.stop();
            // 记录方法结束
            if (log.isInfoEnabled()) {
                try {
                    log.info("支付订单服务方法结束 - 方法: {}, 订单号: {}, 耗时: {} ms, 结果: {}", 
                            methodName, orderNo, stopWatch.getTotalTimeMillis(), 
                            result != null ? toJson(result) : "null");
                } catch (Exception e) {
                    log.warn("记录方法结束日志异常", e);
                }
            }
        }
    }
    
    /**
     * 提取方法参数
     */
    private Map<String, Object> extractMethodParams(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        Parameter[] parameters = method.getParameters();
        Map<String, Object> paramMap = new HashMap<>(parameters.length);
        
        for (int i = 0; i < parameters.length && i < args.length; i++) {
            paramMap.put(parameters[i].getName(), args[i]);
        }
        
        return paramMap;
    }
    
    /**
     * 获取字符串参数
     */
    private String getStringParam(Map<String, Object> paramMap, String paramName) {
        Object value = paramMap.get(paramName);
        return (value instanceof String) ? (String) value : null;
    }
    
    /**
     * 设置业务ID
     */
    private void setBusinessId(String businessId) {
        if (!StringUtils.isEmpty(businessId)) {
            MDCTraceUtils.setBusinessId(businessId);
        }
    }
    
    /**
     * 将对象转换为JSON字符串
     */
    private String toJson(Object obj) throws JsonProcessingException {
        return objectMapper.writeValueAsString(obj);
    }
} 